
if __name__ == "__main__":
    import os
    os.system("python MY_MC3.py")
    exit()

accc = 0
from direct.actor.Actor import Actor

from panda3d.core import AmbientLight, DirectionalLight
from panda3d.core import Vec3, Vec4, Point3, LPoint3
# ~ from panda3d.core import PTAVecBase3f
from panda3d.core import TransformState
from panda3d.core import BitMask32
from panda3d.core import NodePath, PandaNode

from panda3d.core import CardMaker
from panda3d.core import TexturePool, Texture
from panda3d.core import Material
from panda3d.core import GeomNode, Geom
from panda3d.core import GeomLines, GeomTriangles
from panda3d.core import GeomVertexData, GeomVertexWriter, GeomVertexFormat
from panda3d.core import ColorBlendAttrib, TransparencyAttrib
from panda3d.core import ShaderAttrib, Shader, ShaderBuffer
from panda3d.core import GraphicsPipe, GraphicsOutput
from panda3d.core import OmniBoundingVolume
from panda3d.core import SamplerState

from panda3d.bullet import BulletWorld
from panda3d.bullet import BulletBoxShape, BulletPlaneShape, BulletSphereShape, BulletCapsuleShape, BulletCylinderShape
from panda3d.bullet import BulletRigidBodyNode, BulletGhostNode, BulletCharacterControllerNode
from panda3d.bullet import BulletDebugNode
from panda3d.bullet import ZUp
from panda3d.bullet import BulletGenericConstraint

import sys
import math
from itertools import count
import numpy

from class1 import BlockManager

def intv(v):
    x, y, z = v
    # 坑爹，panda3d的低精度向量运算有误差，int()有时会相差1
    return round(x), round(y), round(z)

@global_reg
class PageGame(Page):
    def __init__(self, root):
        self.root = root
        self.world = None
        self.bulletworld = None
        self.debugp = None
        
        self.character = None
        # ~ self.help_pos = None
        
        self.speedx = 0
        self.speedy = 0
        self.cubef = None
        
        #self.block_data = {}
        
        self.booms = []
        
        
    def load(self, mes):
        

        # Input
        
        self.move_control = MoveControl(self)
        self.fixed_control = FixedControl(self)
        self.fixed_control.reg()
        self.move_control.reg()
        self.hands_control = HandsControl(self)
        self.hands_control.reg()
        
        # Task
        self.root.update_list.append(self.update)
        # ~ self.root.reg_timer(2, self.setenemyapi)
        
        # Physics
        self.make_scene()
        
        i = self.block_manager.physics_interest_new(self.character.get_pos())
        t = self.glowCamera.get_transform()
        p2 = t.get_mat().xform_point(Vec3(0, 4, 0.8))
        i2 = self.block_manager.physics_interest_new(p2)
        
        def block_physics():
            # ~ self.block_manager.physics_interest_update(i, self.character.get_pos())
            t = self.character.get_transform()
            p1 = t.get_mat().xform_point(Vec3(0, 0, -0.4))
            self.block_manager.physics_interest_update(i, p1)
            t = self.glowCamera.get_transform()
            p2 = t.get_mat().xform_point(Vec3(0, 4, 0.8))
            self.block_manager.physics_interest_update(i2, p2)
            self.block_manager.physics_render()
            
        self.root.reg_timer(1/10, block_physics)
    
    def make_scene(self):
        if "light":
            
            K = 0.3
            alight = AmbientLight('ambientLight')
            alight.set_color(Vec4(K, K, K, 1))
            alightp = render.attach_new_node(alight)
            
            
            
            K = 0.8
            dlight1 = DirectionalLight('directionalLight')
            dlight1.set_direction(Vec3(1, 1.6, -0.65))
            dlight1.set_color(Vec4(K, K, K, 1))
            dlightp1 = render.attach_new_node(dlight1)
            
            dlight2 = DirectionalLight('directionalLight2')
            dlight2.set_direction(Vec3(-1, -1.6, -0.65))
            dlight2.set_color(Vec4(K, K, K, 1))
            dlightp2 = render.attach_new_node(dlight2)

            render.clearLight()
            render.setLight(alightp)
            render.setLight(dlightp1)
            render.setLight(dlightp2)
            
        
        

        # World
        self.world = render.attach_new_node('World')
        
        self.debugp = self.world.attach_new_node(BulletDebugNode('Debug'))
        # ~ self.debugp.show()

        self.bulletworld = BulletWorld()
        self.bulletworld.set_gravity(Vec3(0, 0, -9.81 * 3))
        self.bulletworld.set_debug_node(self.debugp.node())
        
        # zhun xin
        tex = TexturePool.loadTexture("models/zhunxin.png")
        cm = CardMaker("zhunxin")
        cm.setFrame(-8, 8, -8, 8)
        cmn = self.root.pixel2d.attach_new_node(cm.generate())
        cmn.setTexture(tex)
        hw, hh = self.root.hsize
        cmn.setPos(hw, 0,    -hh )
        cmn.setTransparency(TransparencyAttrib.MAlpha)
        
        # Ground
        # ~ shape = BulletPlaneShape(Vec3(0, 0, 1), 0)
        # ~ img = PNMImage(Filename('models/elevation2.png'))
        # ~ shape = BulletHeightfieldShape(img, 0, ZUp)
        shape = BulletBoxShape(Vec3(50, 50 ,1))
        np = self.world.attach_new_node(BulletRigidBodyNode('Ground'))
        np.node().addShape(shape)
        # ~ np.node().set_debug_enabled(False)
        np.setPos(0, 0, -2)
        np.setCollideMask(BitMask32.allOn())
        self.bulletworld.attach_rigid_body(np.node())

        
        
        # Character
        
        def cha():
            pass
        
        h = 1.8
        w = 0.4
        # ~ shape = BulletCapsuleShape(w, h - 2 * w, ZUp)
        
        shape = BulletCylinderShape(w, h, ZUp)
        # ~ shape = BulletBoxShape(Vec3(w, w, h))
        # ~ self.charactern = BulletCharacterControllerNode(shape, 0.4, "player")       # character node
        self.charactern = BulletRigidBodyNode("player")       # character node
        self.charactern.addShape(shape)
        # ~ enemy.setGravity(10)
        self.charactern.setMass(1.0)
        self.charactern.set_friction(0)
        # ~ self.charactern.set_restitution(0.09)
        
        
        frameB = TransformState.makePosHpr(Point3(0, 0, 0), Vec3(0, 0, 0))

        generic = BulletGenericConstraint(self.charactern, frameB, True)
        generic.setLinearLimit(0, 0, -1)
        generic.setLinearLimit(1, 0, -1)
        generic.setLinearLimit(2, 0, -1)
        generic.setAngularLimit(0, 0, 0)
        generic.setAngularLimit(1, 0, 0)
        generic.setAngularLimit(2, 0, -1)
        self.bulletworld.attach_constraint(generic)
        
        
        # ~ self.charactern.setGravity(10)
        # ~ self.charactern.set_max_slope(20)
        self.character = self.world.attach_new_node(self.charactern)
        # ~ self.character.setPos(-30, 0, 0)
        self.character.setPos(-17, 0, 2)
        self.help_pos = Vec3(-17, 0, 2)
        # ~ self.character.setH(45)
        # ~ self.character.setH(270)
        # ~ self.characterNP.setCollideMask(BitMask32.allOn())
        self.character.setCollideMask(BitMask32(0b00000111))        # character mask
        # ~ self.bulletworld.attach_character(self.charactern)
        self.bulletworld.attach_rigid_body(self.charactern)

        # ~ self.actor = Actor('models/ralph/ralph.egg', {
                                                 # ~ 'run' : 'models/ralph/ralph-run.egg',
                                                 # ~ 'walk' : 'models/ralph/ralph-walk.egg',
                                                 # ~ 'jump' : 'models/ralph/ralph-jump.egg'})
        # ~ self.actor.reparentTo(self.characterNP)         # do not show myself
        # ~ self.actor.setScale(0.3048) # 1ft = 0.3048m
        # ~ self.actor.setH(180)
        # ~ self.actor.setPos(0, 0, -1)
        
        """
        # character_help - ghost
        shape_help = BulletCapsuleShape(w, h + 0.05 - 2 * w, ZUp)
        self.characterhn = BulletGhostNode("player_help")       # character node
        self.characterhn.addShape(shape_help)
        self.characterh = self.world.attach_new_node(self.characterhn)
        # ~ self.character.setPos(-30, 0, 0)
        self.characterh.setPos(-17, 0, 2)
        # ~ self.character.setH(45)
        # ~ self.character.setH(270)
        # ~ self.characterNP.setCollideMask(BitMask32.allOn())
        self.characterh.setCollideMask(BitMask32(0b00011000))
        self.bulletworld.attach_ghost(self.characterhn)
        """
        
        # base block
        
        myMaterial = Material()
        myMaterial.setShininess(5.0)
        myMaterial.setAmbient((1, 1, 1, 1)) #Make this material blue

        
        
        ######################################################GLASS#####################
        
        
        self.modelbuffer = self.root.makeFBO("model buffer", 1)
        # ~ self.modelbuffer = self.root.win        # 如果着色器得当，只用主摄像机就可以解决问题（同样可以多渲染目标）
        
        
        
        
        self.texDepth = Texture()
        self.texDepth.setFormat(Texture.FDepthStencil)
        self.texColor = Texture()
        self.texAux0 = Texture()

        self.glowCamera = base.makeCamera(
            self.modelbuffer, lens=base.cam.node().getLens())
        
        # ~ print(self.root.help_camera)
        # ~ self.render.set_camera(self.glowCamera)
        # ~ print(self.root.help_camera)
        self.root.help_camera = self.glowCamera
        # ~ print(self.root.help_camera)
        
        tempnode = NodePath(PandaNode("temp node"))
        tempnode.setShader(Shader.load(Shader.SL_GLSL, "shaders/common.vert", "shaders/common.frag"))
        tempnode.set_shader_input("attr_light0_color",  dlight1.get_color())
        tempnode.set_shader_input("attr_light0_vec", dlight1.get_direction())
        tempnode.set_shader_input("attr_light1_color", dlight2.get_color())
        tempnode.set_shader_input("attr_light1_vec", dlight2.get_direction())
        tempnode.set_shader_input("attr_material_ambient", myMaterial.get_ambient())
        tempnode.set_shader_input("attr_ambient", alight.get_color())
        tempnode.set_shader_input("attr_colorscale", 1.0)
        tempnode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.M_none, ColorBlendAttrib.O_fbuffer_alpha, ColorBlendAttrib.O_incoming_alpha))
        
        self.glowCamera.node().setInitialState(tempnode.getState())
        
        self.modelbuffer.addRenderTexture(self.texDepth,
            GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepthStencil)
        self.modelbuffer.addRenderTexture(self.texColor,
            GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)
        self.modelbuffer.addRenderTexture(self.texAux0,
            GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPAuxRgba0)
        #self.modelbuffer.addRenderTexture(self.texNormal,
            #GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPDepth)
        
        self.modelbuffer.setSort(1)
        self.modelbuffer.setClearColor((0, 0, 0, 1))
        self.modelbuffer.set_clear_active(self.modelbuffer.RTPAuxRgba0, True)
        
        
        
        if "后处理":
            base.cam.node().set_active(False)
            if "card":
                vdata = GeomVertexData('card_data', GeomVertexFormat.getV3(), Geom.UHStatic)
                vertex = GeomVertexWriter(vdata, 'vertex')
                vertex.addData3f(-1, 0, 1)
                vertex.addData3f(1, 0, 1)
                vertex.addData3f(-1, 0, -1)
                vertex.addData3f(1, 0, -1)
                
                prim = GeomTriangles(Geom.UHStatic)
                prim.addVertices(0, 2, 3)
                prim.addVertices(0, 3, 1)
                geom = Geom(vdata)
                geom.addPrimitive(prim)

                card = GeomNode('card')
                card.addGeom(geom)

            self.finalbuffer = self.root.makeFBO("final buffer", 2)
            self.finalbuffer.set_sort(2)
            self.texFinal = Texture()
            self.finalbuffer.addRenderTexture(self.texFinal,
                GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)
                
            p = NodePath(card)
            p.set_pos(0, 2, 0)
            p.set_color(Vec4(1, 1, 1, 1))
            p.set_scale(Vec3(0.5, 0.5, 0.5) * 6)
                # ~ p.reparent_to(render)
            
            self.finalCamera = base.makeCamera(
                self.finalbuffer, lens=base.cam.node().getLens(), scene=p)
            self.finalCamera.set_pos(Vec3(0, 0, 0))
            self.finalCamera.set_hpr(Vec3(0, 0, 0))
            
            p.setShader(Shader.load(Shader.SL_GLSL, "shaders/common.vert", "shaders/compose.frag"))
            p.set_shader_input("layer_color",  self.texColor)
            p.set_shader_input("layer_aux0", self.texAux0 )
            p.set_shader_input("layer_depth",  self.texDepth)
            # ~ p.set_shader_input("texpad",  (1000, 800))
            p.set_shader_input("texpad",  (1024, 1024))
            
            # ~ self.card = self.finalbuffer.getTextureCard()
            # ~ self.card.setTexture(self.texFinal)
            # ~ self.card.reparentTo(render2d)
            
            # pass 2
            self.finalbuffer2 = self.root.makeFBO("final buffer2", 3)
            self.finalbuffer2.set_sort(3)
            self.texFinal2 = Texture()
            self.finalbuffer2.addRenderTexture(self.texFinal2,
                GraphicsOutput.RTMBindOrCopy, GraphicsOutput.RTPColor)
                
            p2 = NodePath(card.__copy__())
            p2.set_pos(0, 2, 1)
            p2.set_color(Vec4(1, 1, 1, 1))
            p2.set_scale(Vec3(0.5, 0.5, 0.5) * 6)
            # ~ p2.reparent_to(self.world)
            
            self.finalCamera2 = base.makeCamera(
                self.finalbuffer2, lens=base.cam.node().getLens(), scene=p2)
            self.finalCamera2.set_pos(Vec3(0, 0, 1))
            self.finalCamera2.set_hpr(Vec3(0, 0, 0))
            
            p2.setShader(Shader.load(Shader.SL_GLSL, "shaders/common.vert", "shaders/compose2.frag"))
            p2.set_shader_input("layer_color",  self.texFinal)
            p2.set_shader_input("texpad",  (1024, 1024))
            
            self.card = self.finalbuffer2.getTextureCard()
            self.card.setTexture(self.texFinal2)
            self.card.reparentTo(render2d)
        
        
        #skybox
        
        
        # ~ myMaterial = Material()
        # ~ myMaterial.set_shininess(0)
        # ~ myMaterial.set_diffuse(Vec3(0, 0, 0)) #Make this material blue
        # ~ myMaterial.set_ambient(Vec4(1, 1, 1, 1) / 0.3) #Make this material blue
        
        
        
        sky = loader.load_model("models/sky.egg")
        
        sky.set_scale(10)
        sky.set_bin('background', 0)
        sky.set_depth_write(False)
        sky.set_depth_test(False)
        sky.set_shader_off(1)
        sky.set_compass()
        # ~ sky.setMaterial(myMaterial)
        sky.setShader(Shader.load(Shader.SL_GLSL, "shaders/common_tex.vert", "shaders/skybox.frag"))
        sky.reparent_to(self.glowCamera)
        
        # ~ self.skybox = loader.loadModel("models/skybox.egg")
        # ~ self.skybox.reparentTo(self.glowCamera)
        # ~ self.skybox.set_two_sided(True)
        # ~ #self.skybox.set_bin("background", 0)
        # ~ self.skybox.set_depth_write(False)
        # ~ self.skybox.set_compass()
        # ~ self.skybox.set_color(Vec3(0.5, 0.5, 0.8))
        
        # cube frame
        if "choose cube frame":
        
            vdata = GeomVertexData('cube_data', GeomVertexFormat.getV3(), Geom.UHStatic)
            vertex = GeomVertexWriter(vdata, 'vertex')
            vertex.addData3f(-0.5, -0.5, -0.5)
            vertex.addData3f(0.5, -0.5, -0.5)
            vertex.addData3f(0.5, 0.5, -0.5)
            vertex.addData3f(-0.5, 0.5, -0.5)
            vertex.addData3f(-0.5, -0.5, 0.5)
            vertex.addData3f(0.5, -0.5, 0.5)
            vertex.addData3f(0.5, 0.5, 0.5)
            vertex.addData3f(-0.5, 0.5, 0.5)
            
            prim = GeomLines(Geom.UHStatic)

            prim.addVertices(0, 1)
            prim.addVertices(1, 2)
            prim.addVertices(2, 3)
            prim.addVertices(3, 0)
            prim.addVertices(4, 5)
            prim.addVertices(5, 6)
            prim.addVertices(6, 7)
            prim.addVertices(7, 4)
            prim.addVertices(0, 4)
            prim.addVertices(1, 5)
            prim.addVertices(2, 6)
            prim.addVertices(3, 7)
            
            geom = Geom(vdata)
            geom.addPrimitive(prim)

            cubefn = GeomNode('cubef')
            cubefn.addGeom(geom)
            
            self.cubef = self.world.attach_new_node(cubefn)
            self.cubef.set_color(Vec3(0, 0, 0))
            # ~ self.cubef.set_scale(Vec3(1.005, 1.005, 1.005))
            self.cubef.hide()
        
        
        
        if "buffer debug":
            self.root.bufferViewer.setPosition("llcorner")
            self.root.bufferViewer.setCardSize(0, 0.40)
            self.root.bufferViewer.setLayout("vline")
            
            def f():
                self.root.bufferViewer.toggleEnable()
                self.modelbuffer.setClearColorActive(True)
                self.modelbuffer.setClearDepthActive(True)
                # ~ self.modelbuffer.set_clear_stencil_active(True)
            self.root.accept("f12", f)
            
            
        
        if "instancing 2":
            self.block_manager = BlockManager(self.world, self.bulletworld)
        
        def rg(x, y):
            return numpy.arange(x, y, dtype=int)
        # 建筑x
        
        if "build rooms":
            # ~ for x in rg(-4, 5):
                # ~ for y in rg(-4, 5):
            for x in rg(-3, 4):
                for y in rg(-3, 4):
            # ~ for x in rg(-2, 3):
                # ~ for y in rg(-2, 3):
            # ~ for x in rg(-1, 2):
                # ~ for y in rg(-1, 2):
                    for z in rg(0, 3) * 8:
                    # ~ for z in rg(0, 1) * 8:
                    
            # ~ for x in rg(0, 1):
                # ~ for y in rg(0, 1):
                    
                        r1 = rg(-5, 6) + 20 * x
                        r2 = rg(-5, 6) + 20 * y
                        for i in r1:
                            for j in r2:
                                self.setblockapi((i, j, -1 + z), flush=False)
                                self.setblockapi((i, j, 5 + z), flush=False)
                        for i in r1:
                            for j in r2:
                                a = min(abs(r1[0] - i), abs(r1[-1] - i))
                                b = min(abs(r2[0] - j), abs(r2[-1] - j))
                                if a == 0  and b == 0:
                                    self.setblockapi((i, j, 0 +z ), flush=False)
                                elif a == 0  or b == 0:
                                    self.setblockapi((i, j, 0 + z), 2, flush=False)
                                    
            
            # ~ for i in range(-1, 2):
                # ~ for j in range(1):
                    # ~ for k in range(3):
                        # ~ self.setblockapi((i, j, k), 2)
        print(accc)
        
        self.block_manager.flush()
        
        if "Performance Testing" and False:
            acc = 0
            N = 11
            for k in range(N):
                k2 = N - k - 1
                for i in range(-k2, k2 + 1):
                    for j in range(-k2, k2 + 1):
                        self.setblockapi((i, j, k))
                        acc += 1
            print("方块数：", acc)

    def update(self, dt):
        dt = globalClock.getDt()
        
        self.help_pos = self.character.get_pos()
        self.bulletworld.doPhysics(dt, 4, 1./240.)
            
        
        # 手雷
        
        l2 = []
        for t in self.booms:
            boom, bh1, bh2 = t
            trans = boom.get_transform()
            bh1.set_transform(trans)
            bh2.set_transform(trans)
            
            if bh1.getNumOverlappingNodes() > 0:
                print("boom fall")
                p_me = boom.get_transform().get_pos()
                for rb in bh2.getOverlappingNodes():
                    if rb.has_python_tag("enemyhook"):
                        print("boom 1")
                        p_he = rb.get_transform().get_pos()
                        d = p_he - p_me
                        
                        # ~ n.set_python_tag("blood", blood)
                        rb.set_active(True)
                        l = d.length()
                        print("boom length", l)
                        print("boom v", (10 / (l + 2)) * 80)
                        d.normalize()
                        v = d * (10 / (l + 2)) * 80
                        
                        v.z = v.z * 2
                        rb.apply_impulse(v, Point3(0, 0, 0))
                self.bulletworld.removeRigidBody(boom)
                self.bulletworld.removeGhost(bh1)
                self.bulletworld.removeGhost(bh2)
                l2.append(t)
        
        for boom in l2:
            self.booms.remove(boom)
                        
        # character ghost
        # ~ trans = self.character.get_transform()
        # ~ self.characterh.set_transform(trans)
        

    def reg_timer(self, name, t, cb):
        self.timer[name] = [t, t, cb]
    
    def unreg_timer(self, name):
        del self.timer[name]
        
    
    def cleanup(self):
        self.bulletworld = None
        self.world.removeNode()


    def setblockapi(self, t, type=1, flush=True):
        x, y, z = t
        if not isinstance(x, (int, numpy.integer)):
            raise TypeError("setblockapi need (int, int, int)")
        
        self.block_manager.setblock(t, type, flush)
        global accc
        accc += 1
                
        return 
        
        
            
    def getblockapi(self, t):
        return self.block_manager.getblock(t)

    def delblockapi(self, t, flush=True):
        self.block_manager.delblock(t, flush)
        
    
    def setenemyapi(self):
        import random
        r = random.random
        p = Point3(r() * 20, r() * 20, 3)
        
        h = 1.8
        w = 0.4
        shape = BulletCapsuleShape(w, h - 2 * w, ZUp)
        enemy = BulletRigidBodyNode("enemy")
        enemy.addShape(shape)
        # ~ enemy.setGravity(10)
        enemy.setMass(10.0)
        enemyp = self.world.attach_new_node(enemy)
        enemy.set_python_tag("enemyhook", enemyp)
        enemy.set_python_tag("blood", 3)
        enemyp.setPos(p)
        enemyp.setH(90)
        enemyp.setCollideMask(BitMask32(0b01100001))    # enemy mask
        self.bulletworld.attach_rigid_body(enemy)

        ap = self.actor.copyTo(render)
        # ~ ap = self.cubef.copyTo(self.render)
        ap.reparentTo(enemyp)
        ap.setScale(0.3048) # 1ft = 0.3048m
        ap.setH(180)
        ap.setPos(0, 0, -1)
        
        frameB = TransformState.makePosHpr(Point3(0, 0, 0), Vec3(0, 0, 0))

        generic = BulletGenericConstraint(enemy, frameB, True)
        generic.setLinearLimit(0, 0, -1)
        generic.setLinearLimit(1, 0, -1)
        generic.setLinearLimit(2, 0, -1)
        generic.setAngularLimit(0, 0, 0)
        generic.setAngularLimit(1, 0, 0)
        generic.setAngularLimit(2, 0, 0)
        
        
        enemy.set_python_tag("constrainthook", generic)
        self.bulletworld.attach_constraint(generic)
    
    def cmd(self):
        pass


class FixedControl(Control):
    def __init__(self, page):
        self.page = page
        self.root = page.root
        
    def reg(self):
        self.root.accept('escape', self.do_exit)
        self.root.accept('f3', self.toggle_debug)
        
        
        
    def unreg(self):
        self.root.ignore("escape")
        self.root.ignore("f3")
        self.root.ignore("f12")
        
    def do_exit(self):
        self.page.cleanup()
        sys.exit(1)

        
    def toggle_debug(self):
        if self.page.debugp.is_hidden():
            self.page.debugp.show()
        else:
            self.page.debugp.hide()


class MoveControl(Control):
    def __init__(self, page):
        self.page = page
        self.root = page.root
        self.jumping = False
        self.jumping_start = False
        self._bug = 0
        self.temp_speed = Vec3(0, 0, 0)
        
        
    def reg(self):
        hx, hy = self.root.hsize
        if self.mouse_update not in self.root.mouse_update_list:
            self.root.mouse_update_list.append(self.mouse_update)
            self.root.update_list.append(self.update)
        else:
            raise Exception()
        
        @self.root.accept2("w")
        def f():
            self.page.speedy = 1
        @self.root.accept2("w-up")
        def f():
            if self.page.speedy == 1:
                self.page.speedy = 0
        @self.root.accept2("s")
        def f():
            self.page.speedy = -1
        @self.root.accept2("s-up")
        def f():
            if self.page.speedy == -1:
                self.page.speedy = 0
        @self.root.accept2("d")
        def f():
            self.page.speedx = 1
        @self.root.accept2("d-up")
        def f():
            if self.page.speedx == 1:
                self.page.speedx = 0
        @self.root.accept2("a")
        def f():
            self.page.speedx = -1
        @self.root.accept2("a-up")
        def f():
            if self.page.speedx == -1:
                self.page.speedx = 0
        
        @self.root.accept2("space")
        def jump():
            if self.jumping == True:
                return
            self.page.charactern.set_active(True)
            self.page.charactern.apply_impulse(Vec3(0, 0, 11), Point3(0, 0, 0))
            self.jumping = True
            
            print("jump")
            self.jumping_start = True
            def ticker(dt):
                ticker.acc += dt
                print("jump", ticker.acc)
                if ticker.acc > 0.2:
                    self.jumping_start = False
                    self.root.update_list.remove(ticker)
            ticker.acc = 0
            self.root.update_list.append(ticker)

        print("move reg")
        
        globals()["debug_dh"] = 0
        
        
    def unreg(self):
        self.root.mouse_update_list.remove(self.mouse_update)
        self.root.update_list.remove(self.update)
        self.root.ignore("w")
        self.root.ignore("w-up")
        self.root.ignore("s")
        self.root.ignore("s-up")
        self.root.ignore("d")
        self.root.ignore("d-up")
        self.root.ignore("a")
        self.root.ignore("a-up")
        
        self.page.speedx = 0
        self.page.speedy = 0
        self.page.charactern.setLinearVelocity(Vec3(0 ,0, 0))
        print("move unreg")
    
    
    def update(self, dt):
        t = self.page.character.get_transform()
        m = t.get_mat()
        p = m.xform_point(Vec3(0, 0, 0.8))
        base.cam.set_pos(p)
        # ~ base.cam.setPos(t.get_mat().xform_point(Vec3(0, -4, 4)))        # 第三人称        
        self.page.glowCamera.set_pos(p)
        
        speed = Vec3(0, 0, 0)
        speed.set_x(self.page.speedx)
        speed.set_y(self.page.speedy)
        speed.normalize()
        speed *= 5
        speed = m.xform_vec(speed)
        
        real_speed = (self.page.character.get_pos() - self.page.help_pos) / dt
        real_speed.z = 0
        
        self.page.charactern.set_active(True)
        
         
        h1 = self.page.character.get_transform().get_hpr()[0]
        h2 = self.page.glowCamera.get_h()
        dh = h2 - h1
        if dh > 300:
            dh -= 360
        elif dh < -300:
            dh += 360
        
        if abs(dh) > 0.1:
            # ~ print(dh)
            self.page.charactern.set_angular_velocity(dh  )
        else:
            self.page.charactern.set_angular_velocity(0)
        
        
        base_speed = self.page.charactern.get_linear_velocity()
        if base_speed.length() > 20:
            print("speed bug")
            base_speed = base_speed.normalized() * 20
        # ~ elif base_speed.length() > 1:
            # ~ print("speed debug", base_speed)
        
        
        entries = self.page.bulletworld.contactTest(self.page.charactern, True).getContacts()        # 着地检测
        wall = False
        land = False
        for collision in entries:
            mpoint = collision.getManifoldPoint()
            newPos = mpoint.getPositionWorldOnA()
            vec = mpoint.getLocalPointA()
            stepHeight2 = vec.z
            # ~ print(stepHeight2, "land")
            # 人物的高度是1.8
            bottom = -1.8 / 2
            if bottom - 0.15 < stepHeight2 < bottom + 0.15:
                if vec.x ** 2 + vec.y ** 2 < 0.39 ** 2:     # 圆柱侧边碰撞
                    land = True
            else:
                wall = True

        
        if not self.jumping and land:
            # ~ print("c1")
            self._bug = 0
            self.page.charactern.set_linear_velocity(speed)
        elif not self.jumping and not land:
            # 由于bullet物理不稳定，在墙角经常误判踩空，所以判断2次才算踩空
            if self._bug < 30 :
                self._bug += 1
            else:
                print("c2 踩空")
                self.jumping = True
                self.page.charactern.set_linear_velocity(base_speed + speed - self.temp_speed)
                self._bug = 0
        elif self.jumping and not land:
            
            
            # ~ if wall and self.wall == False:
            # ~ if wall:
                #self.wall = True
                # ~ print("碰壁")
                # ~ print(self.temp_speed)
                # ~ print(base_speed)
                # ~ print(speed)
                # ~ print(self.temp_speed)
                # ~ all.x = 0
                # ~ all.y = 0
                # ~ self.temp_speed = Vec3(0, 0, 0)
            all = base_speed + speed - real_speed
            self.page.charactern.set_linear_velocity(all)
        elif self.jumping and land:
            # ~ if base_speed.get_z() < 0.05:
            if not self.jumping_start:
                print("c4 落地")
                print("debug", bottom, stepHeight2)
                self.jumping = False
                
                self.page.charactern.set_linear_velocity(speed)
                p = self.page.character.get_pos()
                # ~ p.z = newPos.z + 0.9
                # ~ self.page.character.set_pos(p)
            else:
                print("c42", base_speed)
                self.page.charactern.set_linear_velocity(base_speed + speed - self.temp_speed)
        else:
            raise Exception()
            
        
        
        
        self.temp_speed = speed
        
        
    
    def mouse_update(self, dxy, p0, v):
        dx, dy = dxy
        dh = - dx * 0.1
        dp = - dy * 0.1
        
        # camera update        
        self.page.glowCamera.set_p(self.page.glowCamera.get_p() + dp)
        self.page.glowCamera.set_h(self.page.glowCamera.get_h() + dh)
        # ~ base.cam.set_p(base.cam.get_p() + dp)
        # ~ base.cam.set_h(self.page.character.get_h())
        
        
        
        p_from = p0 - v
        p_to = p0 + v * 10
        result = self.page.bulletworld.ray_test_closest(p_from, p_to, BitMask32(0b00011000))      # 只有ghost的末尾不是1
        if result.hasHit():
            n = result.getNode()
            if n.has_python_tag("block"):
                p = result.getHitPos()                                              
                p = p + result.getHitNormal() * (-0.05)
                x = p.x // 1 + 0.5
                y = p.y // 1 + 0.5
                z = p.z // 1 + 0.5
                self.page.cubef.set_pos(Vec3(x, y, z) - v * 0.005)
                self.page.cubef.show()
            else:
                self.page.cubef.hide()
        else:
            self.page.cubef.hide()
                
            

class Tool:
    # name: int
    # pic: texture
    
    # ~ def __init__(self, name, pic):
        # ~ self.name = name
        # ~ self.pic = TexturePool.loadTexture(pic)
        
    def hold(self):
        raise NotImplementedError()
        
    def release(self):
        raise NotImplementedError()
        

frame_pic = TexturePool.loadTexture("models/frame.png")
class HandsControl(Control):
    def __init__(self, page):
        self.page = page
        self.root = page.root
        self.left = None
        self.right = [None, None, None, None, None]
        self.point = 1
        
        
        self.right[0] = Gun()
        self.right[1] = BuildStick()
        self.right[2] = BuildBoss()
        self.right[3] = BS2()
        
        self.obj_frames = [None, None, None, None, None]
        self.obj_slots = [None, None, None, None, None]
        
        
    def reg(self):
        self.root.accept("1", self.change, [0])
        self.root.accept("2", self.change, [1])
        self.root.accept("3", self.change, [2])
        self.root.accept("4", self.change, [3])
        self.root.accept("5", self.change, [4])
        self.handbar = self.root.aspect2d.attach_new_node("handbar")
        
        # ~ self.handbar.hide()
        cm_handslot = CardMaker("handslot")
        cm_handslot.set_frame(-0.06, 0.06, -0.06, 0.06)
        cm_handslotframe = CardMaker("handslotfram")
        cm_handslotframe.set_frame(-0.06, 0.06, -0.06, 0.06)
        for i in range(5):
            tool = self.right[i]
            if tool is None:
                tool = Tool.hand
            s = self.handbar.attach_new_node(cm_handslot.generate())
            s.setTexture(tool.pic)
            sf = self.handbar.attach_new_node(cm_handslotframe.generate())
            sf.setTexture(frame_pic)
            self.obj_frames[i] = sf
            self.obj_slots[i] = s
            
            
            x = (-0.5 + i * 0.2) * 0.6
            y = - 0.85
            
            s.setPos(x, 0,    y )
            
            sf.setPos(x, -1,    y )
            sf.setTransparency(TransparencyAttrib.MAlpha)
            if i == self.point:
                sf.set_color(1, 1, 1, 1)
            else:
                sf.set_color(1, 1, 1, 0.5)
                
        tool = self.right[self.point]
        if tool is None:
            tool = Tool.hand
        tool.hold(self.page)
        
        
        
    def unreg(self):
        self.root.ignore("1")
        self.root.ignore("2")
        self.root.ignore("3")
        self.root.ignore("4")
        self.root.ignore("5")
        self.handbar.remove_node()
    
    def change(self, n):
        print("change", n)
        
        if n == self.point:
            return
        
        tool = self.right[self.point]
        if tool is None:
            tool = Tool.hand
        tool.release(self.page)
        
        self.point = n
        for i, tool in enumerate(self.right):
            if i == self.point:
                self.obj_frames[i].set_color(1, 1, 1, 1)
            else:
                self.obj_frames[i].set_color(1, 1, 1, 0.5)
                
        tool = self.right[self.point]
        if tool is None:
            tool = Tool.hand
        tool.hold(self.page)
            
        
class Hand(Tool):
    def __init__(self):
        self.name = "hand"
        self.pic = TexturePool.loadTexture("models/hand.png")
    
    def hold(self, page):
        pass
        
    def release(self, page):
        pass


Tool.hand = Hand()


class Gun(Tool):
    def __init__(self):
        self.name = "gun"
        self.pic = TexturePool.loadTexture("models/gun.png")
        self.ball = loader.loadModel('models/pyramid.egg')
        
        
    
    def hold(self, page):
        base.accept('mouse1', self.shoot1, [page])
        base.accept('mouse3', self.shoot2, [page])
        
    def release(self, page):
        base.ignore("mouse1")
        base.ignore("mouse3")
        
    
    def shoot1(self, page):
        pFrom = Point3()
        pTo = Point3()
        base.camLens.extrude(Point3(0, 0, 0), pFrom, pTo)

        pFrom = render.getRelativePoint(base.help_camera, pFrom)
        pTo = render.getRelativePoint(base.help_camera, pTo)
        # ~ pFrom = render.getRelativePoint(base.cam, pFrom)
        # ~ pTo = render.getRelativePoint(base.cam, pTo)
        v = pTo - pFrom
        v.normalize()
        pTo = pFrom + v * 100
        pFrom = pFrom + v
        
        # Raycast for closest hit
        result = page.bulletworld.ray_test_closest(pFrom, pTo, BitMask32(0b00000001))
        print("#")
        print( result.hasHit())
        print(result.getHitFraction())
        print(result.getNode())
        print(result.getHitPos())
        print(result.getHitNormal())
        print("#\n")
        
        if result.hasHit():
            p = result.getHitPos()
            n = result.getNode()
            if n.has_python_tag("enemyhook"):
                np = n.get_python_tag("enemyhook")
                c = n.get_python_tag("constrainthook")
                blood = n.get_python_tag("blood")
                blood -= 1
                if blood <= 0:
                    np.removeNode()
                    page.bulletworld.remove(n)
                    page.bulletworld.removeConstraint(c)
                    vp = self.ball.copyTo(render)
                    vp.setPos(p)
                else:
                    n.set_python_tag("blood", blood)
                    n.set_active(True)
                    n.apply_impulse(v * 50 + Vec3(0, 0, 50), Point3(0, 0, 0))
                    


    def shoot2(self, page):
        # Get from/to points from mouse click
        # ~ pMouse = base.mouseWatcherNode.getMouse()
        # ~ print(type(pMouse))
        pFrom = Point3()
        pTo = Point3()
        base.camLens.extrude(Point3(0, 0, 0), pFrom, pTo)

        # ~ pFrom = render.getRelativePoint(base.cam, pFrom)
        # ~ pTo = render.getRelativePoint(base.cam, pTo)

        pFrom = render.getRelativePoint(base.help_camera, pFrom)
        pTo = render.getRelativePoint(base.help_camera, pTo)
        # Calculate initial velocity
        v = pTo - pFrom
        v.normalize()

        # Create bullet
        # ~ shape = BulletBoxShape(Vec3(0.5, 0.5, 0.5))
        shape = BulletSphereShape(0.05)
        bullet = BulletRigidBodyNode('Bullet')
        bullet.addShape(shape)
        bullet.setMass(2.0)
        bullet.setGravity(1.0)
        
        # ~ bullet.set_restitution(0)
        # ~ bullet.set_friction(10)
        
        bullet.setLinearVelocity(v * 30)
        bulletp = page.world.attach_new_node(bullet)
        bulletp.setPos(pFrom + v * 1.5)
        bulletp.setCollideMask(BitMask32(0b00000001))
        # 抗高速穿过
        bullet.setCcdMotionThreshold(1e-7);
        bullet.setCcdSweptSphereRadius(0.50);

        page.bulletworld.attach_rigid_body(bullet)
        
        shape = BulletSphereShape(0.2)
        bh1 = BulletGhostNode('BulletHelp1')
        bh1.addShape(shape)
        # ~ bh1.setCcdMotionThreshold(1e-7);
        # ~ bh1.setCcdSweptSphereRadius(0.50);
        bh1p = page.world.attach_new_node(bh1)
        bh1p.setPos(pFrom + v * 1)
        bh1p.setCollideMask(BitMask32(0b00101010))
        
        
        page.bulletworld.attach_ghost(bh1)
        
        # ~ shape = BulletSphereShape(5)
        shape = BulletSphereShape(2)
        bh2 = BulletGhostNode('BulletHelp2')
        bh2.addShape(shape)
        bh2p = page.world.attach_new_node(bh2)
        bh2p.setPos(pFrom + v * 1)
        bh2p.setCollideMask(BitMask32(0b01010100))
        
        page.bulletworld.attach_ghost(bh2)
        
        t = (bullet, bh1, bh2)
        page.booms.append(t)
        
        def rm(task):
            if t in page.booms:
                page.booms.remove(t)
                page.bulletworld.removeRigidBody(bullet)
                page.bulletworld.removeGhost(bh1)
                page.bulletworld.removeGhost(bh2)
            return task.done

        taskMgr.doMethodLater(5, rm, 'doRemove', 
            appendTask=True)


    
        
class BuildStick(Tool):
    def __init__(self):
        self.name = "buildstick"
        self.pic = TexturePool.loadTexture("models/build.png")
    
    def hold(self, page):
        base.accept('mouse1', self.del_block, [page])
        base.accept('mouse3', self.set_block, [page])
        
    def release(self, page):
        base.ignore("mouse1")
        base.ignore("mouse3")
        
        
    def del_block(self, page):
        pFrom = Point3()
        pTo = Point3()
        # ~ base.camLens.extrude(Point3(0, 0, 0), pFrom, pTo)
        base.help_camera.node().get_lens().extrude(Point3(0, 0, 0), pFrom, pTo)

        pFrom = render.getRelativePoint(base.help_camera, pFrom)
        pTo = render.getRelativePoint(base.help_camera, pTo)
        v = pTo - pFrom
        v.normalize()
        pTo = pFrom + v * 10
        pFrom = pFrom - v
        
        # Raycast for closest hit
        result = page.bulletworld.ray_test_closest(pFrom, pTo, BitMask32(0b00011000))
        print("#del")
        print( result.hasHit())
        print(result.getHitFraction())
        print(result.getNode())
        print(result.getHitPos())
        print(result.getHitNormal())
        print("#\n")
        
        if result.hasHit():
            p = result.getHitPos()
            n = result.getNode()
            if n.has_python_tag("block"):
                p = p - result.getHitNormal() * 0.05
                x = p.x // 1
                y = p.y // 1
                z = p.z // 1
                if page.getblockapi((x, y ,z)) is not None:
                    page.delblockapi((x, y ,z))
                else:
                    print("#del no exist block?")
            else:
                pass
      



    def set_block(self, page):
        pFrom = Point3()
        pTo = Point3()
        # ~ base.camLens.extrude(Point3(0, 0, 0), pFrom, pTo)
        base.help_camera.node().get_lens().extrude(Point3(0, 0, 0), pFrom, pTo)

        pFrom = render.getRelativePoint(base.help_camera, pFrom)
        pTo = render.getRelativePoint(base.help_camera, pTo)
        v = pTo - pFrom
        v.normalize()
        pTo = pFrom + v * 10
        pFrom = pFrom - v
        
        # Raycast for closest hit
        result = page.bulletworld.ray_test_closest(pFrom, pTo, BitMask32(0b00011000))
        print("#set")
        print( result.hasHit())
        print(result.getHitFraction())
        print(result.getNode())
        print(result.getHitPos())
        print(result.getHitNormal())
        print("#\n")
        
        if result.hasHit():
            n = result.getNode()
            if n.has_python_tag("block"):
                p = result.getHitPos()
                p = p + result.getHitNormal() * 0.95
                x = round(p.x // 1)
                y = round(p.y // 1)
                z = round(p.z // 1)
                
                # 检查和玩家的碰撞
                m = page.block_manager
                m.help_box.set_pos(Vec3(x + 0.5, y + 0.5, z + 0.5))
                m.bulletworld.attach_rigid_body(m.help_boxn)
                entries = m.bulletworld.contactTest(m.help_boxn, True).getContacts()
                
                m.bulletworld.remove_rigid_body(m.help_boxn)
                if entries:
                    print("can't set block", entries)
                    print(entries[0].get_node0(), entries[0].get_node1())
                    return
                
                page.setblockapi((x, y ,z))
                
      

        
class BuildBoss(Tool):
    def __init__(self):
        self.name = "buildboss"
        self.pic = TexturePool.loadTexture("models/builds.png")
        self.apic = TexturePool.loadTexture("models/arrow1.png")
        
        
        self.card = None
        self.ts = None
        self.miss = None
        self.multi = []
        self.single = None
    
    @property
    def mat(self):
        if self.ts is None:
            return None
        else:
            return self.ts.get_mat()
    
    def hold(self, page):
        base.accept('mouse3', self.start, [page])
        base.accept('mouse3-up', self.end, [page])
        assert self.check not in page.root.mouse_update_list
        page.root.mouse_update_list.append(self.check)
        self.page = page
        
        if self.card is None:
            cm = CardMaker("build_arrow")
            cm.setFrame(-0.3, 0.3, -0.3, 0.3)
            card = page.world.attach_new_node(cm.generate())
            card.setTexture(self.apic)
            card.setPos(0, 0,   0)
            card.setTransparency(TransparencyAttrib.MAlpha)
            card.hide()
            self.card = card
        
        
    def release(self, page):
        page.root.mouse_update_list.remove(self.check)
        base.ignore('mouse3')
        base.ignore('mouse3-up')
        self.card.hide()
        del self.page
        
    def check(self, dxy, p_from, v):
        page = self.page
        
        result = page.bulletworld.ray_test_closest(p_from + v * 1, p_from + v * 10, BitMask32(0b00000001))
        if result.hasHit():
            n = result.get_node()
            if n.has_python_tag("block"):
                p = result.getHitPos()
                no = result.getHitNormal()
                
                p = p + no * (-0.05)
                x = round(p.x // 1)
                y = round(p.y // 1)
                z = round(p.z // 1)
                pos = Vec3(x, y, z)
                
                f = -no
                x = Vec3.up().dot(f)
                if x > 0.9:
                    p = 90
                    h = self.page.character.get_h()
                    h = - round(h / 90) * 90
                elif x < -0.9:
                    p = -90
                    h = self.page.character.get_h()
                    h = round(h / 90) * 90
                elif -0.1 < x < 0.1:
                    x = Vec3.right().dot(f)
                    y = Vec3.forward().dot(f)
                    h = math.atan2(y, x) / math.pi * 180 - 90
                    h = round(h / 90) * 90
                    p = 0
                else:
                    #print("?normal", no)
                    return
                
                
                ts = TransformState.make_pos_hpr(pos, (h, p, 0))
                self.ts = ts
                ts1 = TransformState.make_pos(Vec3(0.5, 0.5, 0.5))
                ts2 = TransformState.make_pos(Vec3(0, -0.505, 0))
                ts3 = TransformState.make_mat(ts2.mat * (ts.mat * ts1.mat))

                self.card.set_transform(ts3)
                self.card.show()
            else:
                self.card.hide()
                self.ts = None
        else:
            self.card.hide()
            self.ts = None
        
    
    def start(self, page):
        if self.ts is None:
            self.miss = True
            return 
        else:
            self.miss = False
            
        page.move_control.unreg()
        
        def f1(ch):
            self.multi.append(ch)
            if len(self.multi) > 2:
                self.multi = self.multi[-2:]
            elif len(self.multi) == 2:
                self.single = False
            
        page.root.accept("w", f1, ["w"])
        page.root.accept("s", f1, ["s"])
        page.root.accept("a", f1, ["a"])
        page.root.accept("d", f1, ["d"])
        page.root.accept("q", f1, ["q"])
        
        def f2(ch):
            if len(self.multi) == 0:
                return
            if self.single == False and len(self.multi) == 1:   # 按了两个键又松开，视为取消
                self.multi.remove(ch)
                return
            if self.multi[-1] == ch:
                self.grow()
            self.multi.remove(ch)
            
        page.root.accept("w-up", f2, ["w"])
        page.root.accept("s-up", f2, ["s"])
        page.root.accept("a-up", f2, ["a"])
        page.root.accept("d-up", f2, ["d"])
        page.root.accept("q-up", f2, ["q"])
        
    def end(self, page):
        if self.miss == True:
            self.miss = None
            return 
        else:
            self.miss = None
            
        
        page.root.ignore("w-up")
        page.root.ignore("s-up")
        page.root.ignore("d-up")
        page.root.ignore("a-up")
        page.root.ignore("q-up")
        page.root.ignore("w")
        page.root.ignore("s")
        page.root.ignore("d")
        page.root.ignore("a")
        page.root.ignore("q")
        
        page.move_control.reg()    
        self.ts = None
        self.multi = []
        self.single = True
        
    
    dir_table = {"w": Vec3.up(), "s": Vec3.down(), "a": Vec3.left(), "d": Vec3.right(), "q": Vec3.forward()}
    
    def grow(self):
        multi = self.multi
        
        if len(multi) == 1:
            dir = self.dir_table[multi[-1]]
            for i in count(1):
                p = intv(self.mat.xform_point(dir * i))
                if not self.page.getblockapi(p):
                    self.page.setblockapi(p)
                    break
        elif len(multi) == 2:
            dir = self.dir_table[multi[-1]]
            dir2 = self.dir_table[multi[-2]]
            x = dir.dot(dir2)
            if not -0.1 < x < 0.1:
                # 两个方向不是垂直关系
                return 
                
            jump = 1
            for i in count(1):
                p = intv(self.mat.xform_point(dir * i))
                if not self.page.getblockapi(p):
                    jump = i
                    break
            #直接摆放时，jump=1
            
            acc = [0]
            for j in count(1):
                flag = None
                for i in range(jump):
                    p = intv(self.mat.xform_point(dir2 * j + dir * i))
                    if self.page.getblockapi(p):
                        continue
                    else:
                        flag = "fail"
                        break
                else:
                    p = intv(self.mat.xform_point(dir2 * j + dir * (i + 1)))
                    if not self.page.getblockapi(p):
                        flag = "success"
                    else:
                        flag = "fail"
                if flag == "success":
                    acc.append(j)
                    continue
                else:
                    break
                        
            for j in count(-1, -1):
                flag = None
                for i in range(jump):
                    p = intv(self.mat.xform_point(dir2 * j + dir * i))
                    if self.page.getblockapi(p):
                        continue
                    else:
                        flag = "fail"
                        break
                else:
                    p = intv(self.mat.xform_point(dir2 * j + dir * (i + 1)))
                    if not self.page.getblockapi(p):
                        flag = "success"
                    else:
                        flag = "fail"
                if flag == "success":
                    acc.append(j)
                    continue
                else:
                    break
                    
            for j in acc:
                p = intv(self.mat.xform_point(dir2 * j + dir * jump))
                self.page.setblockapi(p)
                        
           
        
class BS2(Tool):
    def __init__(self):
        self.name = "bs2"
        self.pic = TexturePool.loadTexture("models/2.png")
    
    def hold(self, page):
        base.accept('mouse1', self.del_block, [page])
        base.accept('mouse3', self.set_block, [page])
        
    def release(self, page):
        base.ignore("mouse1")
        base.ignore("mouse3")
        
        
    def del_block(self, page):
        BuildStick.del_block(self, page)
      



    def set_block(self, page):
        pFrom = Point3()
        pTo = Point3()
        # ~ base.camLens.extrude(Point3(0, 0, 0), pFrom, pTo)
        base.help_camera.node().get_lens().extrude(Point3(0, 0, 0), pFrom, pTo)

        pFrom = render.getRelativePoint(base.help_camera, pFrom)
        pTo = render.getRelativePoint(base.help_camera, pTo)
        v = pTo - pFrom
        v.normalize()
        pTo = pFrom + v * 10
        pFrom = pFrom - v 
        
        # Raycast for closest hit
        result = page.bulletworld.ray_test_closest(pFrom, pTo, BitMask32(0b00011000))
        print("#set")
        print( result.hasHit())
        print(result.getHitFraction())
        print(result.getNode())
        print(result.getHitPos())
        print(result.getHitNormal())
        print("#\n")
        
        if result.hasHit():
            n = result.getNode()
            if n.has_python_tag("block"):
                p = result.getHitPos()
                p = p + result.getHitNormal() * 0.95
                x = round(p.x // 1)
                y = round(p.y // 1)
                z = round(p.z // 1)
                
                page.setblockapi((x, y ,z), 2)
                
      
        
                
                

if __name__ == "__main__":
    game = MyFrame()
    base.run()

